#include "SHA1.h"
#include <endian.h>
#include <cstring>


/* This Algorithm treats byte array as big endian order integer(32bit).
 * If os/cpu architecture is little endian, transform it.
 */

void SHA1::byte_to_int(uint32_t *dest, const uint8_t *src, uint32_t ilen) {
	#if __BYTE_ORDER == __LITTLE_ENDIAN
	for(; ilen--; ++dest, src+=4) {
		*dest = (uint32_t) src[0]<<24 | src[1]<<16 | src[2]<<8 | src[3];
	}
	#else
	if ((const uint8_t*)dest!=src) memcpy(dest, src, (ilen)<<2);
	#endif
}

// transform each int(32bit) into 4-byte(big endian), ilen is length of int
void SHA1::int_to_byte(uint8_t *dest, const uint32_t *src, uint32_t ilen) {
	#if __BYTE_ORDER == __LITTLE_ENDIAN
	uint32_t *idest = (uint32_t*)dest;
	const uint8_t *bsrc = (const uint8_t*)src;
	for(; ilen--; ++idest, bsrc+=4) {
		*idest = (uint32_t)bsrc[0]<<24 | bsrc[1]<<16 | bsrc[2]<<8 | bsrc[3];
	}
	#else
	if (dest!=(uint8_t*)src) memcpy(dest, src, (ilen)<<2);
	#endif
}

void SHA1::transform(const uint8_t *block) {
	register uint32_t a=mDigest[0], b=mDigest[1], 
		c=mDigest[2], d=mDigest[3], e=mDigest[4];
	// transform 4-byte into int(32bit)
	byte_to_int((uint32_t *)mBuffer, block, 16);
	uint32_t *buf = (uint32_t *)mBuffer;

// constants K
#define K1 0x5A827999L
#define K2 0x6ED9EBA1L
#define K3 0x8F1BBCDCL
#define K4 0xCA62C1D6L

// The four core functions - F1 is a little bit optimized
// (as found in Colin Plumbs public domain implementation)
// #define F1(x, y, z) ((x & y) | (~x & z))
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) (x ^ y ^ z)
#define F3(x, y, z) ((x & y) | (z & (x | y)))
#define F4(x, y, z) F2(x, y, z)

// recycle rotate 4-byte(32bit)
#define ROL(x, n) ( ((x) << (n)) | ((x) >> (32-(n))) )

// The central step in SHA1 algorithm
#define SHA1STEP(f, a, b, c, d, e, data) \
	(e+=ROL(a, 5)+f(b, c, d)+data, b=ROL(b, 30))

#define BUF(i) (buf[i&15] = ROL( \
		buf[i&15]^buf[(i-14)&15]^buf[(i-8)&15]^buf[(i-3)&15], 1))

	SHA1STEP(F1, a, b, c, d, e, buf[0] + K1);
	SHA1STEP(F1, e, a, b, c, d, buf[1] + K1);
	SHA1STEP(F1, d, e, a, b, c, buf[2] + K1);
	SHA1STEP(F1, c, d, e, a, b, buf[3] + K1);
	SHA1STEP(F1, b, c, d, e, a, buf[4] + K1);
	SHA1STEP(F1, a, b, c, d, e, buf[5] + K1);
	SHA1STEP(F1, e, a, b, c, d, buf[6] + K1);
	SHA1STEP(F1, d, e, a, b, c, buf[7] + K1);
	SHA1STEP(F1, c, d, e, a, b, buf[8] + K1);
	SHA1STEP(F1, b, c, d, e, a, buf[9] + K1);
	SHA1STEP(F1, a, b, c, d, e, buf[10] + K1);
	SHA1STEP(F1, e, a, b, c, d, buf[11] + K1);
	SHA1STEP(F1, d, e, a, b, c, buf[12] + K1);
	SHA1STEP(F1, c, d, e, a, b, buf[13] + K1);
	SHA1STEP(F1, b, c, d, e, a, buf[14] + K1);
	SHA1STEP(F1, a, b, c, d, e, buf[15] + K1);
	SHA1STEP(F1, e, a, b, c, d, BUF(16) + K1);
	SHA1STEP(F1, d, e, a, b, c, BUF(17) + K1);
	SHA1STEP(F1, c, d, e, a, b, BUF(18) + K1);
	SHA1STEP(F1, b, c, d, e, a, BUF(19) + K1);

	SHA1STEP(F2, a, b, c, d, e, BUF(20) + K2);
	SHA1STEP(F2, e, a, b, c, d, BUF(21) + K2);
	SHA1STEP(F2, d, e, a, b, c, BUF(22) + K2);
	SHA1STEP(F2, c, d, e, a, b, BUF(23) + K2);
	SHA1STEP(F2, b, c, d, e, a, BUF(24) + K2);
	SHA1STEP(F2, a, b, c, d, e, BUF(25) + K2);
	SHA1STEP(F2, e, a, b, c, d, BUF(26) + K2);
	SHA1STEP(F2, d, e, a, b, c, BUF(27) + K2);
	SHA1STEP(F2, c, d, e, a, b, BUF(28) + K2);
	SHA1STEP(F2, b, c, d, e, a, BUF(29) + K2);
	SHA1STEP(F2, a, b, c, d, e, BUF(30) + K2);
	SHA1STEP(F2, e, a, b, c, d, BUF(31) + K2);
	SHA1STEP(F2, d, e, a, b, c, BUF(32) + K2);
	SHA1STEP(F2, c, d, e, a, b, BUF(33) + K2);
	SHA1STEP(F2, b, c, d, e, a, BUF(34) + K2);
	SHA1STEP(F2, a, b, c, d, e, BUF(35) + K2);
	SHA1STEP(F2, e, a, b, c, d, BUF(36) + K2);
	SHA1STEP(F2, d, e, a, b, c, BUF(37) + K2);
	SHA1STEP(F2, c, d, e, a, b, BUF(38) + K2);
	SHA1STEP(F2, b, c, d, e, a, BUF(39) + K2);
	
	SHA1STEP(F3, a, b, c, d, e, BUF(40) + K3);
	SHA1STEP(F3, e, a, b, c, d, BUF(41) + K3);
	SHA1STEP(F3, d, e, a, b, c, BUF(42) + K3);
	SHA1STEP(F3, c, d, e, a, b, BUF(43) + K3);
	SHA1STEP(F3, b, c, d, e, a, BUF(44) + K3);
	SHA1STEP(F3, a, b, c, d, e, BUF(45) + K3);
	SHA1STEP(F3, e, a, b, c, d, BUF(46) + K3);
	SHA1STEP(F3, d, e, a, b, c, BUF(47) + K3);
	SHA1STEP(F3, c, d, e, a, b, BUF(48) + K3);
	SHA1STEP(F3, b, c, d, e, a, BUF(49) + K3);
	SHA1STEP(F3, a, b, c, d, e, BUF(50) + K3);
	SHA1STEP(F3, e, a, b, c, d, BUF(51) + K3);
	SHA1STEP(F3, d, e, a, b, c, BUF(52) + K3);
	SHA1STEP(F3, c, d, e, a, b, BUF(53) + K3);
	SHA1STEP(F3, b, c, d, e, a, BUF(54) + K3);
	SHA1STEP(F3, a, b, c, d, e, BUF(55) + K3);
	SHA1STEP(F3, e, a, b, c, d, BUF(56) + K3);
	SHA1STEP(F3, d, e, a, b, c, BUF(57) + K3);
	SHA1STEP(F3, c, d, e, a, b, BUF(58) + K3);
	SHA1STEP(F3, b, c, d, e, a, BUF(59) + K3);
	
	SHA1STEP(F4, a, b, c, d, e, BUF(60) + K4);
	SHA1STEP(F4, e, a, b, c, d, BUF(61) + K4);
	SHA1STEP(F4, d, e, a, b, c, BUF(62) + K4);
	SHA1STEP(F4, c, d, e, a, b, BUF(63) + K4);
	SHA1STEP(F4, b, c, d, e, a, BUF(64) + K4);
	SHA1STEP(F4, a, b, c, d, e, BUF(65) + K4);
	SHA1STEP(F4, e, a, b, c, d, BUF(66) + K4);
	SHA1STEP(F4, d, e, a, b, c, BUF(67) + K4);
	SHA1STEP(F4, c, d, e, a, b, BUF(68) + K4);
	SHA1STEP(F4, b, c, d, e, a, BUF(69) + K4);
	SHA1STEP(F4, a, b, c, d, e, BUF(70) + K4);
	SHA1STEP(F4, e, a, b, c, d, BUF(71) + K4);
	SHA1STEP(F4, d, e, a, b, c, BUF(72) + K4);
	SHA1STEP(F4, c, d, e, a, b, BUF(73) + K4);
	SHA1STEP(F4, b, c, d, e, a, BUF(74) + K4);
	SHA1STEP(F4, a, b, c, d, e, BUF(75) + K4);
	SHA1STEP(F4, e, a, b, c, d, BUF(76) + K4);
	SHA1STEP(F4, d, e, a, b, c, BUF(77) + K4);
	SHA1STEP(F4, c, d, e, a, b, BUF(78) + K4);
	SHA1STEP(F4, b, c, d, e, a, BUF(79) + K4);
	
	mDigest[0]+=a, mDigest[1]+=b, mDigest[2]+=c, 
		mDigest[3]+=d, mDigest[4]+=e;
}

void SHA1::init() {
	mDigest[0] = 0x67452301;
	mDigest[1] = 0xEFCDAB89;
	mDigest[2] = 0x98BADCFE;
	mDigest[3] = 0x10325476;
	mDigest[4] = 0xC3D2E1F0;
	mBufferSizeLow = mBufferSizeHigh = 0;
}

void SHA1::update(const void *data, unsigned int size) {
	// update bit count
	uint32_t low = mBufferSizeLow;
	mBufferSizeLow += size<<3;
	if (mBufferSizeLow<low) mBufferSizeHigh++;	// carry from low to high
	mBufferSizeHigh += size>>29;
	// handle any leading odd-sized chunks
	const uint8_t *buf = (const uint8_t*)data;
	uint32_t offset = (low>>3) & 63;
	if (offset) {
		if (size<64-offset) {
			memcpy(mBuffer+offset, buf, size);
			return;
		}
		memcpy(mBuffer+offset, buf, 64-offset);
		buf += 64-offset;
		size -= 64-offset;
		transform(mBuffer);
	}
	// process data in 64-byte chunks
	for(; size>63; size-=64, buf+=64) {
		transform(buf);
	}
	// handle any remaining bytes of data. 
	memcpy(mBuffer, buf, size);
}

void SHA1::final() {
	uint32_t offset = (mBufferSizeLow >3) & 63;
	mBuffer[offset++] = 0x80;
	// current chunk no more space to store last Bitcount,
	// fill last space as zero, and digest this chunk.
	if (offset>56) {
		memset(mBuffer+offset, 0, 64-offset);
		offset = 0;
		transform(mBuffer);
	}
	// fill first padding 56-byte, copy Bitcount to last 8-byte
	memset(mBuffer+offset, 0, 56-offset);
	int_to_byte(mBuffer+56, &mBufferSizeHigh, 1);
	int_to_byte(mBuffer+60, &mBufferSizeLow, 1);
	transform(mBuffer);
	// message digest result
	int_to_byte((uint8_t*)mDigest, mDigest, 5);
	// clear sensitive message
	memset(mBuffer, 0, sizeof(mBuffer));
	mBufferSizeLow = mBufferSizeHigh = 0;
}